package com.TanNgee.spring;

import com.TanNgee.annotation.RpcReference;
import com.TanNgee.annotation.RpcService;
import com.TanNgee.config.RpcServiceConfig;
import com.TanNgee.extension.ExtensionLoader;
import com.TanNgee.factory.SingletonFactory;
import com.TanNgee.provider.ServiceProvider;
import com.TanNgee.provider.impl.ZkServiceProviderImpl;
import com.TanNgee.proxy.RpcClientProxy;
import com.TanNgee.remoting.transport.RpcRequestTransport;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

/**
 * 创建一个Bean对象之前调用这个方法看这个类是否有  RpcService RpcReference 注解
 * <p>
 * Spring bean 在实例化之前会调用 postProcessBeforeInitialization()方法，
 * 在 Spring bean 实例化之后会调用  postProcessAfterInitialization() 方法。
 *
 * @author 陈毅
 * @create 2022/3/21 15:10
 */
@Slf4j
@Component
public class SpringBeanPostProcessor implements BeanPostProcessor {

    private final ServiceProvider serviceProvider;
    private final RpcRequestTransport rpcClient;

    public SpringBeanPostProcessor() {
        this.serviceProvider = SingletonFactory.getInstance(ZkServiceProviderImpl.class);
        this.rpcClient = ExtensionLoader.getExtensionLoader(RpcRequestTransport.class).getExtension("netty");
    }

    /**
     * 实例化之前调用的方法
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @SneakyThrows
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean.getClass().isAnnotationPresent(RpcService.class)) {
            log.info("[{}] is annotated with  [{}]", bean.getClass().getName(), RpcService.class.getCanonicalName());

            // get RpcService annotation
            RpcService rpcService = bean.getClass().getAnnotation(RpcService.class);
            // build RpcServiceProperties
            RpcServiceConfig rpcServiceConfig = RpcServiceConfig.builder()
                    .group(rpcService.group())
                    .version(rpcService.version())
                    .service(bean).build();

            serviceProvider.publishService(rpcServiceConfig);
        }
        return bean;
    }

    /**
     * 实例化之后调用的方法
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetClass = bean.getClass();
        Field[] declaredFields = targetClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            RpcReference rpcReference = declaredField.getAnnotation(RpcReference.class);

            if (rpcReference != null) {
                RpcServiceConfig rpcServiceConfig = RpcServiceConfig.builder()
                        .group(rpcReference.group())
                        .version(rpcReference.version()).build();


                RpcClientProxy rpcClientProxy = new RpcClientProxy(rpcClient, rpcServiceConfig);

                Object clientProxy = rpcClientProxy.getProxy(declaredField.getType());
                declaredField.setAccessible(true);

                try {
                    declaredField.set(bean, clientProxy);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        }
        return bean;
    }
}
